//
// Created by YueChuan on 2023/5/1.
//

#include "tcp_server.h"
#include "log.h"

namespace yuechuan {
    static ConfigVar<uint64_t>::ptr g_tcp_server_read_timeout =
            Config::Lookup("tcp_server.read_timeout", (uint64_t) (60 * 1000 * 2), "tcp server read timeout");
    static Logger::ptr g_logger = YUECHUAN_LOG_ROOT();

    TcpServer::TcpServer(IOManager *worker, IOManager *ioWork, IOManager *acceptWork) :
            m_work(worker),
            m_ioWorker(ioWork),
            m_acceptWorker(acceptWork),
            m_recvTimeout(g_tcp_server_read_timeout->getValue()),
            m_name("yuechuan/1.0.0"),
            m_isStop(true) {

    }

    TcpServer::~TcpServer() {
        for (auto &i: m_socks) {
            i->close();
        }
        m_socks.clear();
    }

    void TcpServer::setConf(const TcpServerConf &v) {
        m_conf.reset(new TcpServerConf(v));
    }

    bool TcpServer::bind(Address::ptr addr, bool ssl) {
        std::vector<Address::ptr> addrs;
        std::vector<Address::ptr> fails;
        addrs.emplace_back(addr);
        return bind(addrs, fails, ssl);
    }

    bool TcpServer::bind(const std::vector<Address::ptr> &addrs, std::vector<Address::ptr> &fails, bool ssl) {
        m_ssl = ssl;
        for (auto &addr: addrs) {
            Socket::ptr sock = ssl ? SSLSocket::CreateTcp(addr) : Socket::CreateTCP(addr);
            if (!sock->bind(addr)) {
                YUECHUAN_LOG_ERROR(g_logger) << "bind fail errno = " << errno
                                             << " " << strerror(errno)
                                             << " addr = " << addr->toString();
            }
            if (!sock->listen()) {
                YUECHUAN_LOG_ERROR(g_logger) << "listen fail errno = " << errno
                                             << " " << strerror(errno)
                                             << " addr = " << addr->toString();
                fails.emplace_back(addr);
                continue;
            }
            m_socks.emplace_back(sock);
        }

        if (!fails.empty()) {
            m_socks.clear();
            return false;
        }

        for (auto &i: m_socks) {
            YUECHUAN_LOG_INFO(g_logger) << "type = " << m_type
                                        << " name = " << m_name
                                        << " ssl = " << m_ssl
                                        << " server bind success: " << *i;
        }
        return true;
    }

    void TcpServer::startAccept(Socket::ptr sock) {
        while (!m_isStop) {
            Socket::ptr client = sock->accept();
            if (client) {
                client->setRecvTimeout(m_recvTimeout);
                m_ioWorker->schedule(std::bind(&TcpServer::handleClient, shared_from_this(), client));
            } else {
                YUECHUAN_LOG_ERROR(g_logger) << "accept errno = " << errno << " " << strerror(errno);
            }
        }
    }

    bool TcpServer::start() {
        if (!m_isStop) {
            return true;
        }
        m_isStop = false;
        for (auto &sock : m_socks) {
            m_acceptWorker->schedule(std::bind(&TcpServer::startAccept, shared_from_this(), sock));
        }
        return true;
    }

    void TcpServer::stop() {
        m_isStop = true;
        auto self = shared_from_this();
        m_acceptWorker->schedule([this, self]() {
            for (auto &sock: m_socks) {
                sock->cancelAll();
                sock->close();
            }
            m_socks.clear();
        });
    }

    void TcpServer::handleClient(Socket::ptr client) {
        YUECHUAN_LOG_INFO(g_logger) << "handleClient: " << *client;
    }

    bool TcpServer::loadCertificates(const std::string &cert_file, const std::string &key_file) {
        for (auto &i: m_socks) {
            auto ssl_socket = std::dynamic_pointer_cast<SSLSocket>(i);
            if (ssl_socket) {
                if (!ssl_socket->loadCertificates(cert_file, key_file)) {
                    return false;
                }
            }
        }
        return true;
    }

    std::string TcpServer::toString(const std::string &prefix) {
        std::stringstream ss;
        ss << prefix << "[type= " << m_type
           << " name=" << m_name << " ssl=" << m_ssl
           << " worker=" << (m_work ? m_work->getName() : "")
           << " accept=" << (m_acceptWorker ? m_acceptWorker->getName() : "")
           << " recv_timeout=" << m_recvTimeout << "]" << std::endl;
        std::string pfx = prefix.empty() ? "   " : prefix;
        for (auto &i: m_socks) {
            ss << pfx << pfx << *i << std::endl;
        }
        return ss.str();
    }
}
